package main

import (
	"encoding/json"
	"github.com/garyburd/redigo/redis"
	"github.com/kataras/iris"
	"github.com/ngaut/log"
	"github.com/shopspring/decimal"
	"global/function"
	"global/structs"
	"sort"
	"time"
)

type Depth struct {
	ctx         *Kline
	pair        structs.CoinPair
	buyTicker   *time.Ticker
	saleTicker  *time.Ticker
}

func NewDepth(ctx *Kline, coinPair structs.CoinPair) *Depth {
	return &Depth{
		ctx:         ctx,
		pair:        coinPair,
		buyTicker:   time.NewTicker(3 * time.Second),
		saleTicker:  time.NewTicker(3 * time.Second),
	}
}

func (d Depth) Run() {

	// 计算深度计时器
	go func() {
		for {
			select {
			case <-d.buyTicker.C:
				d.calc("buy")
			case <-d.saleTicker.C:
				d.calc("sale")
			}
		}
	}()

	iris.RegisterOnInterrupt(func() {
		d.buyTicker.Stop()
		d.saleTicker.Stop()
	})
}

type TrustTemp struct {
	Id     int
	Number string
	Price  decimal.Decimal
}

type TmpDecimalDetail struct {
	Number decimal.Decimal
	Price  decimal.Decimal
}

func (d *Depth) Stop() {
	d.buyTicker.Stop()
	log.Debug("time ticker buy stoped")
	d.saleTicker.Stop()
	log.Debug("time ticker sale stoped")
}

func (d *Depth) clear() {
	conn := d.ctx.GetRedisPool("ote").Get()
	defer conn.Close()

	// 目录只考虑8位深度的情况
	keyBuy := function.GetDepthKey(d.pair.Name, "buy", 8)
	keySale := function.GetDepthKey(d.pair.Name, "sale", 8)

	if lengthBuy, err := redis.Int(conn.Do("LLEN", keyBuy)); err != nil {
		log.Errorf("get trust depth length error, msg: %s", err.Error())
	} else {
		if lengthBuy > 100 {
			_, err := conn.Do("LTRIM", keyBuy, 0, 99)
			if err != nil {
				log.Errorf("ltrim trust depth error, msg: %s", err.Error())
			}
		}
	}

	lengthSale, err := redis.Int(conn.Do("LLEN", keySale))
	if err != nil {
		log.Errorf("get trust depth length error, msg: %s", err.Error())
		return
	}

	if lengthSale > 100 {
		_, err := conn.Do("LTRIM", keySale, 0, 99)
		if err != nil {
			log.Errorf("ltrim trust depth error, msg: %s", err.Error())
		}
	}

}

func (d *Depth) calc(flag string) {
	conn := d.ctx.GetRedisPool("ote").Get()
	if conn == nil {
		return
	}
	defer conn.Close()

	// 获取数据
	var key string
	if flag == "buy" {
		key = function.GetBuyTrustDetailKey(d.pair.Name)
	} else {
		key = function.GetSaleTrustDetailKey(d.pair.Name)
	}

	maps, err := redis.StringMap(conn.Do("HGETALL", key))
	if err != nil {
		log.Errorf("get buy trusts error: %s", err)
		return
	}

	depthKey := function.GetDepthKey(d.pair.Name, flag, 8)

	if len(maps) == 0 {
		if exists, _ := redis.Bool(conn.Do("EXISTS", depthKey)); exists {
			conn.Do("DEL", depthKey)
		}
		return
	}

	tmpDepth := make(map[string]TmpDecimalDetail)
	for k := range maps {
		var value structs.TrustCoin
		err = json.Unmarshal([]byte(maps[k]), &value)
		if err != nil {
			log.Errorf("json unmarshal trust item error, msg: %s", err.Error())
			continue
		}

		p, _ := decimal.NewFromString(value.Price)
		n, _ := decimal.NewFromString(value.NumberAvailable)

		key := p.StringFixedBank(8)

		if val, ok := tmpDepth[key]; !ok {
			tmpDepth[key] = TmpDecimalDetail{
				Number: n,
				Price:  p,
			}
		} else {
			tmpDepth[key] = TmpDecimalDetail{
				Number: n.Add(val.Number),
				Price:  p,
			}
		}
	}

	temps := make([]TrustTemp, 0)
	for _, v := range tmpDepth {
		temps = append(temps, TrustTemp{
			Number: v.Number.StringFixedBank(4),
			Price:  v.Price,
		})
	}

	if flag == "buy" {
		// 按价格逆序排序
		sort.Slice(temps, func(i, j int) bool {
			return temps[i].Price.GreaterThan(temps[j].Price)
		})
	} else {
		// 按价格顺序排序
		sort.Slice(temps, func(i, j int) bool {
			return temps[i].Price.LessThan(temps[j].Price)
		})
	}

	conn.Send("MULTI")
	conn.Send("DEL", depthKey)
	// 考虑数据库过大，只保存100条数据
	var count = 0
	for _, v := range temps {
		if count >= 100 {
			break
		}
		bytes, err := json.Marshal(structs.Depth{
			Price:  v.Price.StringFixedBank(int32(d.pair.FloatPrice)),
			Number: v.Number,
		})

		if err != nil {
			log.Errorf("json marshal tmp trust item error, msg: %s", err.Error())
			continue
		}
		conn.Send("RPUSH", depthKey, string(bytes))
		count++
	}
	conn.Do("EXEC")
}
